from utils import *
import sqlite3
import os
import config
import datetime
import account
import information
import friends
import signature

ERROR_DB_FILE_NOT_FOUND = 1
ERROR_DB_UPDATE = 2
ERROR_DB_ADD = 3
ERROR_DB_INFO_NOT_FOUND = 4
ERROR_DB_DEL = 5

class databaseError(Exception):
    def __init__(self, code, value):
        self._value = value
        self._code = code
    def __str__(self):
        return "%d : %s"%(code,repr(self._value))
    def getCode(self):
        return self._code

class myDatabase:
    def __init__(self,dbPath):
        self._dbPath = dbPath
        if not os.path.exists(self._dbPath):
            raise databaseError(ERROR_DB_FILE_NOT_FOUND,'Database file not found')
    
    def isExistInformation(self,ID,authorID):
        conn = sqlite3.connect(self._dbPath)
        conn.row_factory = sqlite3.Row
        c = conn.cursor()
        c.execute("select ID from informations where ID=? and author=?", (ID,authorID))
        if c.fetchone():
            c.close()
            conn.close()
            return True
        else:
            c.close()
            conn.close()
            return False
    
    def delInformation(self,ID,authorID):
        #delete in database
        try:
            conn = sqlite3.connect(self._dbPath)
            conn.row_factory = sqlite3.Row
            c = conn.cursor()
            c.execute('DELETE FROM informations WHERE ID=? and author=?', (ID,authorID))
            conn.commit()
            infoOutput('DATABASE : del info (ID:%s,authorID:%s)'%(ID,authorID))
            c.close()
            conn.close()
        except Exception,msg:
            errorOutput('Can\'t delete information (ID:%s,authorID:%s) : %s'%(ID,authorID,msg))
            c.close()
            conn.close()
            raise databaseError(ERROR_DB_DEL,'Can\'t delete information (ID:%s,authorID:%s) : %s'%(ID,authorID,msg))
        return True
    
    def updateInformation(self,infoObj):
        #serialize fr
        #frs = ""
        #for f in infoObj.getFr():
        #    frs += f.getID() + ','
        #insert in database
        try:
            conn = sqlite3.connect(self._dbPath)
            conn.row_factory = sqlite3.Row
            c = conn.cursor()
            c.execute("update informations set fr=?, date=?, data=?, signature=? where ID=? and author=?",(infoObj.getFr().dumps(),infoObj.getDate().isoformat(),infoObj.getData(),infoObj.getSignature().dumps(),infoObj.getID(),infoObj.getAuthor().getID()))
            conn.commit()
            c.close()
            conn.close()
            infoOutput("DATABASE : update info; ID(%s); authorID(%s)"%(infoObj.getID(),infoObj.getAuthor().getID()))
        except Exception,msg:
            errorOutput('Can\'t update information %s : %s'%(infoObj.getID(),msg))
            c.close()
            conn.close()
            raise databaseError(ERROR_DB_UPDATE,'Can\'t update information %s : %s'%(infoObj.getID(),msg))
        return True
         
    def addInformation(self,infoObj):
        #serialize fr
        #frs = ""
        #for f in infoObj.getFr():
        #    frs += f.getID() + ','
        #insert in database
        try:
            conn = sqlite3.connect(self._dbPath)
            conn.row_factory = sqlite3.Row
            c = conn.cursor()
            c.execute("""insert into informations values ('%s','%s','%s','%s','%s','%s')"""%(infoObj.getID(),infoObj.getAuthor().getID(),infoObj.getFr().dumps(),infoObj.getDate().isoformat(),infoObj.getData(),infoObj.getSignature().dumps()))
            conn.commit()
            c.close()
            conn.close()
            infoOutput("DATABASE : set info; ID(%s); authorID(%s)"%(infoObj.getID(),infoObj.getAuthor().getID()))
        except Exception,msg:
            errorOutput('Can\'t add information %s : %s'%(infoObj.getID(),msg))
            c.close()
            conn.close()
            raise databaseError(ERROR_DB_ADD,'Can\'t add information %s : %s'%(infoObj.getID(),msg))
        return True
    
    def getRawData(self,ID,authorID):
        """Look for data in database without the construction of info object
        return data as string"""
        #get info in sql database
        conn = sqlite3.connect(self._dbPath)
        conn.row_factory = sqlite3.Row
        c = conn.cursor()
        c.execute('select * from informations where ID=? and author=?', (ID,authorID))
        info = c.fetchone()
        if not info: #if not found
            c.close()
            conn.close()
            errorOutput('Can\'t get information : %s with author %s'%(ID,authorID))
            raise databaseError(ERROR_DB_INFO_NOT_FOUND,'Can\'t get information : %s with author %s'%(ID,authorID))
        c.close()
        conn.close()
        infoOutput("DATABASE : get raw data; ID(%s); authorID(%s)"%(ID,authorID))
        return info["data"]
    
    def getInformation(self,ID,authorID):
        """Look for information object in database
        return information object"""
        #get info in sql database
        conn = sqlite3.connect(self._dbPath)
        conn.row_factory = sqlite3.Row
        c = conn.cursor()
        c.execute('select * from informations where ID=? and author=?', (ID,authorID))
        info = c.fetchone()
        if not info:
            errorOutput('Can\'t get information : %s with author %s'%(ID,authorID))
            c.close()
            conn.close()
            raise databaseError(ERROR_DB_INFO_NOT_FOUND,'Can\'t get information : %s with author %s'%(ID,authorID))
        infoOutput("DATABASE : get info; ID(%s); authorID(%s)"%(ID,authorID))
        #build fr
        fr = friends.friendsGroup([])
        fr.loads(info["fr"])
        #for f in info["fr"].split(',')[:-1]:
        #    fr.append(friends.getGlobalAccount(f))
        #build author account
        author = friends.getGlobalAccount(info["author"])
        #build signature
        signObj = signature.signature()
        signObj.loads(info["signature"])
        #build info object
        return information.Information(info["ID"],author,datetime.datetime.strptime(info["date"],"%Y-%m-%dT%H:%M:%S.%f"),fr,info["data"],signObj)
    
    def getStatus(self):
        """give basic information about database"""
        nbRow = len(self.searchInformations())
        size = os.stat(self._dbPath).st_size
        return { 'nbRow':nbRow, 'size':size }
    
    def searchInformations(self,ID='%',authorID='%'):
        """search for informations in database, based on ID and/or authorID
        return list of tuple (ID,authorID)"""
        #make a search in sql database
        conn = sqlite3.connect(self._dbPath)
        conn.row_factory = sqlite3.Row
        c = conn.cursor()
        c.execute('select ID,author from informations where ID LIKE ? and author LIKE ?', (ID,authorID))
        #parse row to output format
        result = []
        i = 0
        for row in c:
            result.append((row['ID'],row['author']))
            i += 1
        infoOutput("DATABASE : search infos : ID(%s) authorID(%s) : %d match"%(ID,authorID,i))
        c.close()
        conn.close()
        return result
    
def createDatabase():
    """static method to create an empty database.
    !!!!!!!!!!! DESTROY ALL PREVIOUS DATA!!!!!!!!!!!!!!"""

    dbPath = config.getSetting("database","basepath")    
    if os.path.exists(dbPath):
        os.remove(dbPath)
    conn = sqlite3.connect(dbPath)
    c = conn.cursor()
    # Create information table
    c.execute('''create table informations(ID text, author text, fr text, date text, data text, signature text, primary key(ID,author))''')
    
    conn.commit()
    c.close()
    conn.close()
    warningOutput('myDatabase : Create a new database !')

myDb = None

def getMyDatabase():
    global myDb
    if not myDb:
        myDb = myDatabase(config.getSetting("database","basepath"))
    return myDb